/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "GnssHAL_GnssPositioning"

#include <vendor/ecarx/xma/gnss/1.0/IHwGnssPositioningCallback.h>
#include <vendor/ecarx/xma/gnss/1.0/BpHwGnssPositioningCallback.h>

#include "GnssPositioning.h"

namespace vendor {
namespace ecarx {
namespace xma {
namespace gnss {
namespace V1_0 {
namespace implementation {

using ::android::hardware::hidl_string;
using ::vendor::ecarx::xma::gnss::V1_0::StatusCode;

std::mutex GnssPositioning::sMutex;
std::map<ClientId, sp<IGnssPositioningCallback>> GnssPositioning::sCallbacks;

GnssPositioning::GnssPositioning() :
    mDeathRecipient(new HidlDeathRecipient(this)) {}

GnssPositioning::~GnssPositioning() {}

/**
 * Subscribes to TCAM positioning service events.
 *
 * @param callback - listener This client must be called on appropriate event.
 */
Return<StatusCode> GnssPositioning::subscribe(const sp<IGnssPositioningCallback>& callback) {
    StatusCode statusCode = StatusCode::SUCCESS;
    sMutex.lock();
    ClientId clientId = getClientId(callback);
    auto it = sCallbacks.find(clientId);
    if (it == sCallbacks.end()) {
        sCallbacks.emplace(clientId, callback);
        callback->linkToDeath(mDeathRecipient, clientId /*cookie*/);
    }
    sMutex.unlock();
    return statusCode;
}

/**
 * Unsubscribes from TCAM positioning service events.
 *
 * @param callback - listener This client must be called on appropriate event.
 */
Return<StatusCode> GnssPositioning::unsubscribe(const sp<IGnssPositioningCallback>& callback) {
    StatusCode statusCode = StatusCode::SUCCESS;
    sMutex.lock();
    ClientId clientId = getClientId(callback);
    auto it = sCallbacks.find(clientId);
    if (it != sCallbacks.end()) {
        it->second->unlinkToDeath(mDeathRecipient);
        sCallbacks.erase(it);
    }
    sMutex.unlock();

    return statusCode;
}

ClientId GnssPositioning::getClientId(const sp<IGnssPositioningCallback>& callback) {
    if (callback->isRemote()) {
        BpHwGnssPositioningCallback* hwCallback =
            static_cast<BpHwGnssPositioningCallback*>(callback.get());
        return static_cast<ClientId>(reinterpret_cast<intptr_t>(hwCallback->onAsBinder()));
    } else {
        return static_cast<ClientId>(reinterpret_cast<intptr_t>(callback.get()));
    }
}

void GnssPositioning::onPOIInfoNotification(const char* poiName, int64_t poiLongitude,
                                            int64_t poiLatitude, const char* street,
                                            const char* houseNumber, const char* city,
                                            const char* region, const char* postcode,
                                            const char* country) {
    ALOGI(
        "onPOIInfoNotification(poiName:%s, lng:%ld, lat:%ld, street:%s, houseNumber:%s, "
        "city:%s, region:%s, postcode:%s, country:%s",
        poiName, poiLongitude, poiLatitude, street, houseNumber, city, region, postcode, country);

    sMutex.lock();
    for (const auto& callback : sCallbacks) {
        callback.second->onPOIInfoNotification(hidl_string(poiName), poiLongitude, poiLatitude,
                                              hidl_string(street), hidl_string(houseNumber),
                                              hidl_string(city), hidl_string(region),
                                              hidl_string(postcode), hidl_string(country));
    }
    sMutex.unlock();
}

void GnssPositioning::handleHidlDeath(uint64_t cookie) {
    sMutex.lock();
    auto it = sCallbacks.find(cookie);
    if (it != sCallbacks.end()) {
        it->second->unlinkToDeath(mDeathRecipient);
        sCallbacks.erase(it);
    }
    sMutex.unlock();
}

}  // namespace implementation
}  // namespace V1_0
}  // namespace gnss
}  // namespace xma
}  // namespace ecarx
}  // namespace vendor
